/*
 * MessageOutputStream.java   ver    28/set/2009
 * 
 * Copyright (c) 2009 mentalsmash.org
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * The use of the Apache License does not indicate that this project is
 * affiliated with the Apache Software Foundation.
 */ 
package org.mentalsmash.tazio.net.cs;

import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.Arrays;

import org.mentalsmash.tazio.net.cs.protocol.NIOObjectContainer;


/**
 * This {@link java.io.OutputStream} implementation writes a Serializable
 * objects to another <tt>OutputStream</tt> using the {@link NIOObjectContainer}
 * serialization protocol, and hiding the details
 * @author Andrea Reale <andrea.reale@gmail.com
 * @version 0.0.1
 * @see NIOObjectContainer
 * @see MessageInputStream
 */
public class MessageOutputStream extends OutputStream {

    /**
     * The stream were data is actually written
     */
    private final OutputStream subStream;
    
    /**
     * Create a new <tt>MessageOutputStream</tt> that will write on
     * <code>os</code>
     * @param os the underlying output stream, to be saved for later use.
     */
    public MessageOutputStream(OutputStream os){
	subStream = os;
    }
    
    /**
     * Writes the specified byte (the low eight bits of the argument b) to the underlying output stream.
     * @param b the byte to be written
     * @throws IOException if the write is unsuccessful
     */
    @Override
    public void write(int b) throws IOException {
	Byte toWrite = new Integer(b).byteValue();
	subStream.write(encapsulate(toWrite));
	
    }
    

    /**
     * Writes <code>len</code> bytes from the specified byte array starting 
     * at offset <code>off</code> to the underlying output stream.
     * <p>
     *  If b is <code>null<code>, a {@link NullPointerException} is thrown.
     * </p>
     * <p>
     * If <code>off</code> is negative, or <code>len</code> is negative, 
     * or <code>off+len</code> is greater than the
     * length of the array <code>b</code>, then an 
     * {@link IndexOutOfBoundsException} is thrown.
     * </p>
     * @param b the data. 
     * @param off the start offset in the data.
     * @param len the number of bytes to write.
     * @throws IOException if an I/O error occurs.
     */
    @Override
    public void write(byte[] b, int off, int len) throws IOException {
	byte[] toWrite = Arrays.copyOfRange(b, off, off+len-1);
	subStream.write(encapsulate(toWrite));
    }
    
    /**
     * Calls <code>flush()</code> on the underlying stream
     * @throws IOException if an I/O error occurs.
     */
    @Override
    public void flush() throws IOException {
	subStream.flush();
    }
    
    /**
     * Calls {@link #flush()} on this instance, and then calls
     * <code>close()</code> on the underlying stream
     */
    @Override
    public void close() throws IOException {
	flush();
	subStream.close();
    }
    
    /**
     * Write any serializable object to the underlying stream.
     * This object can later be retrieved using a
     * {@link MessageInputStream}
     * @param object the message to be written on the underlying stream
     * @throws IOException if an I/O error occurs.
     */
    public void writeMessage(Serializable object) throws IOException{
	subStream.write(encapsulate(object));
    }
    
    /**
     * Encapsulates the payload into a NIOObjectContainer that is the one object
     * that is responsible to serialize the payload according to its specific
     * protocol
     * @param payload the payload to serialize
     * @return the message encapsulated in a <code>byte[]</code>
     * @throws IOException if an I/O error occurs.
     */
    private byte[] encapsulate(Serializable payload) throws IOException{
	NIOObjectContainer container = new NIOObjectContainer(payload);
	byte[] encapsulated = container.writeContainer();
	
	return encapsulated;
    }

}
